/* eslint-disable */
<template>
  <div class="content" :style="{'width':width+'px', 'height':height+'px'}">
    <div class='y-axis' :style="{'height':ctxHeight+'px'}">
      <div class="values">
      <div class="value"
        v-for="(value, key) of yAxisShow"
        :key="key" :style="{'top': value[1] - 12 +'px'}"
        >{{ value[0] }}</div>
      </div>
      <div class="ruler" :height="ctxHeight">
        <div
        v-for="(value, key) of yAxisShow"
        :key="key+'_'" :style="{'top': value[1] + 'px'}"
        ></div>
      </div>
      </div>
      <div style="float: left">
        <canvas id="heatMap" :width="ctxWidth" :height="ctxHeight" v-on:mousemove="toolTip"></canvas>
        <canvas id="colorBar" :width="30" :height="ctxHeight"></canvas>
        <div class="x-axis" :width="ctxWidth">
          <div class='ruler' v-for="(value, key) of xAxisShow"
            :key="key" :style="{'position':'absolute', 'left': value[1] + 'px'}"
            ></div
          >
          <div class='values' v-for="(value, key) of xAxisShow"
            :key="key+'_'" :style="{'position':'absolute', 'left': value[1] - 2 + 'px'}"
            >{{value[0]}}</div
          ></div>
      </div>
  <div class='checked-div' :style="checkedDiv"></div>
  <div class='value-div' :style="valueDiv">
    <div><span>Value:</span> {{ checkedValue.value }} </div>
    <div><span>X:</span> {{ checkedValue.x }} </div>
    <div><span>Y:</span> {{ checkedValue.y }}</div>
  </div>
  </div>
</template>

<script>
export default {
  name: 'HeatMap',
  props: ['mapData', 'xAxis', 'yAxis', 'width', 'height'], // 热力图二位数据，横轴坐标，纵轴坐标，组件宽度，组件高度
  data () {
    return {
      msg: 'Welcome to Your Vue.js App',
      // mapData: [],
      dx: [], // delta
      dy: [],
      xAxis: [],
      yAxis: [],
      xAxisShow: [], // 要显示的坐标
      yAxisShow: [],
      sumX: 0,
      sumY: 0,
      dataMax: 0,
      dataMin: 0,

      x_axis_: [], // css px坐标
      y_axis_: [],

      colorBarData: [], // 色板
      ctxHeatMap: null, // canvas对象
      ctxColorBar: null, // canvas对象
      ctxWidth: null, // canvas 宽度
      ctxHeight: null,
      checkedDiv: {left: 0, top: 0, width: 0, height: 0, display: 'none'},
      valueDiv: {left: 0, top: 0, display: 'none'},
      checkedValue: {}
    }
  },
  created () {
    if (!this.width) this.width = 560
    if (!this.height) this.height = 450

    this.ctxWidth = this.width - 30 - 60 // canvas 大小 colorBar 30px
    this.ctxHeight = this.height - 30
  },
  mounted () {
    this.initColorBar()
    this.initCanvas()
    this.banlanceGrid()
    // this.initGrid()
    this.drawBar()
    this.draw()
  },
  methods: {
    initColorBar () {
      this.colorBarData = []
      for (let i = 0; i < 256; i++) {
        this.colorBarData.push('#00' + this.decToHex(i) + 'ff')
      }
      for (let i = 0; i < 256; i++) {
        this.colorBarData.push('#' + this.decToHex(i) + 'ff' + this.decToHex(255 - i))
      }
      for (let i = 0; i < 256; i++) {
        this.colorBarData.push('#ff' + this.decToHex(255 - i) + '00')
      }
    },
    decToHex (value) {
      value = value.toString(16)
      return value.length === 1 ? '0' + value : value
    },
    initCanvas () {
      this.heatMap = document.getElementById('heatMap')
      this.colorBar = document.getElementById('colorBar')
      this.ctxHeatMap = this.heatMap.getContext('2d')
      this.ctxColorBar = this.colorBar.getContext('2d')
    },
    banlanceGrid () { // 均匀坐标
      let that = this
      this.sumX = this.xAxis.slice(-1) - this.xAxis[0]
      this.sumY = this.yAxis.slice(-1) - this.yAxis[0]
      let xAxisShowLength = Math.ceil(this.ctxWidth / 90) | 1
      let yAxisShowLength = Math.ceil(this.ctxHeight / 60) | 1
      Array.from(new Array(xAxisShowLength + 1).keys()).forEach((v) => {
        let value = (this.sumX / xAxisShowLength) * v + this.xAxis[0]
        if (value !== undefined) {
          let position = that.heatMap.offsetLeft + (value - this.xAxis[0]) / this.sumX * this.ctxWidth
          that.xAxisShow.push([this.roundValue(value), position])
        }
      })
      Array.from(new Array(yAxisShowLength + 1).keys()).forEach((v) => {
        let value = (this.sumY / yAxisShowLength) * v + this.yAxis[0]
        if (value !== undefined) {
          let position = that.heatMap.offsetTop + (1 - (value - this.yAxis[0]) / this.sumY) * this.ctxHeight
          that.yAxisShow.push([this.roundValue(value), position])
        }
      })
    },
    initGrid () { // 计算显示坐标 //非均匀坐标
      let that = this
      let xAxisShowLength = Math.ceil(this.ctxWidth / 90) | 1
      xAxisShowLength = Math.min(xAxisShowLength, this.xAxis.length)
      let yAxisShowLength = Math.ceil(this.ctxHeight / 40) | 1
      yAxisShowLength = Math.min(yAxisShowLength, this.yAxis.length)
      let gap = Math.ceil(this.xAxis.length / xAxisShowLength) | 1
      Array.from(new Array(xAxisShowLength).keys()).forEach((v) => {
        let value = that.xAxis[gap * v]
        if (value !== undefined) {
          let position
          if (that.xAxis.length === that.mapData[0].length) position = that.heatMap.offsetLeft + v / xAxisShowLength * that.ctxWidth
          else position = that.heatMap.offsetLeft + (value - that.xAxis[0]) / (that.xAxis.slice(-1) - that.xAxis[0]) * that.ctxWidth
          that.xAxisShow.push([that.roundValue(value), position])
        }
      })
      gap = Math.ceil(this.yAxis.length / yAxisShowLength) | 1
      Array.from(new Array(yAxisShowLength).keys()).forEach((v) => {
        let value = that.yAxis[gap * v]
        if (value !== undefined) {
          let position
          if (that.yAxis.length === that.mapData.length) position = that.heatMap.offsetTop + (1 - v / yAxisShowLength) * this.ctxHeight
          else position = that.heatMap.offsetTop + (1 - (value - this.yAxis[0]) / (this.yAxis[this.yAxis.length - 1] - this.yAxis[0])) * this.ctxHeight
          that.yAxisShow.push([this.roundValue(value), position])
        }
      })
    },
    roundValue (value) { // 科学计数法处理数字
      if (value === 0) return 0
      if (value.toString().replace('-', '').replace('.', '').length <= 4) {
        return value
      } else {
        value = parseFloat(value).toExponential(2)
        if (value.toString().indexOf('e+0') !== -1) {
          value = value.toString().replace('e+0', '')
        }
        if (value.toString().indexOf('e-1') !== -1) {
          value = (parseFloat(value.toString().replace('e-1', '')) / 10).toFixed(3)
        }
      }
      return value
    },
    getColor (data) { // 计算颜色
      if(this.dataMin !== undefined) [this.dataMax, this.dataMin] = this.getMaxMin(this.mapData)
      let index = parseInt((data - this.dataMin) / (this.dataMax - this.dataMin) * (this.colorBarData.length - 1))
      return this.colorBarData[index]
    },
    draw () { // 画热力图
      if (this.xAxis.length === this.mapData[0].length) { // 坐标点形式，按均匀网格处理
        this.xAxis.forEach((v, i) => { 
          this.dx.push(this.ctxWidth / this.xAxis.length) // 横轴 px值
        }) 
      } else { // 坐标间隔形式，按非均匀处理
        let width_total = this.xAxis.slice(-1)[0] - this.xAxis[0]
        this.xAxis.slice(1).forEach((v, i) => { 
          this.dx.push((this.xAxis[i+1] - this.xAxis[i]) / width_total * this.ctxWidth) 
        }) // 横轴 px值
        console.log('非均匀网格：', this.dx)
      }
      if (this.yAxis.length === this.mapData.length) {
        this.yAxis.forEach((v, i) => { 
          this.dy.push(this.ctxHeight / this.yAxis.length) 
        })
      } else {
        let height_total = this.yAxis.slice(-1)[0] - this.yAxis[0]
        this.yAxis.slice(1).forEach((v, i) => {
          this.dy.push((this.yAxis[i+1] - this.yAxis[i]) / height_total * this.ctxHeight) 
        })
        console.log('非均匀网格：', this.dy)
      }
      let dx = this.dx
      let dy = this.dy.reverse()     // canvas从左上角开始画, 而数据是从左下角开始
      let data = this.mapData.reverse()  // 从左下角开始画
 
      let [x, y] = [0, 0]
      for (let z = 0; z < data.length; z++) {
        x = 0
        let height_ = Math.round(dy[z])
        for (let j = 0; j < data[z].length; j++) {
          let width_ = Math.round(dx[j])
          let color = this.getColor(data[z][j])
          this.ctxHeatMap.fillStyle = color
          this.ctxHeatMap.fillRect(x, y, width_, height_)
          if (z === 0) {
            this.x_axis_.push(x)
          }
          x +=  width_
        }
        this.y_axis_.push(y)
        y += height_
      }
      this.x_axis_.push(x)
      this.y_axis_.push(y)
    },
    drawBar () {
      let height = this.ctxHeight / this.colorBarData.length
      this.colorBarData.forEach((v, i) => {
        this.ctxColorBar.fillStyle = v
        this.ctxColorBar.fillRect(0, this.ctxHeight - i * height, 25, height)
      })
    },
    toolTip (e) {
      let checkedDiv = this.getToolTipPosition(e)
      if (checkedDiv.width === -1) {
        this.toolTipHide()
        return
      }
      this.checkedDiv.left = checkedDiv.left + 'px'
      this.checkedDiv.top = checkedDiv.top + 'px'
      this.checkedDiv.width = checkedDiv.width + 'px'
      this.checkedDiv.height = checkedDiv.height + 'px'

      this.checkedDiv.display = 'none'
      setTimeout(() => { this.checkedDiv.display = 'block' }, 5)

      this.valueDiv.left = checkedDiv.left + 'px'
      this.valueDiv.top = (checkedDiv.top - 70) + 'px'
      this.valueDiv.display = 'block'
      this.checkedValue.value = checkedDiv.value
      this.checkedValue.x = this.roundValue(checkedDiv.x)
      this.checkedValue.y = this.roundValue(checkedDiv.y)
      this.checkedDiv.background = this.getColor(checkedDiv.value)
    },
    toolTipHide (e) {
      console.log('mouseout')
      this.valueDiv.display = 'none'
      this.checkedDiv.display = 'none'
    },
    getToolTipPosition (e) {
      // debugger
      let x = e.x - e.target.offsetLeft + window.scrollX // 点击点相对于canvas的位置
      let y = e.y - e.target.offsetTop + window.scrollY
      let checkedDiv = {left: 0, top: 0, width: 0, height: 0, x: 0, y: 0, value: 0}
      if (x < 0 || x > this.ctxWidth || y < 0 || y > this.ctxHeight) {
        checkedDiv.width = -1
        return checkedDiv
      }

      let [i, j] = [0, 0]
      for (; i < this.x_axis_.length; i++) {
        if (this.x_axis_[i] > x) {
          checkedDiv.left = this.x_axis_[i - 1] + e.target.offsetLeft
          checkedDiv.width = this.x_axis_[i] - this.x_axis_[i - 1]
          break
        }
      }
      for (; j < this.y_axis_.length; j++) {
        if (this.y_axis_[j] > y) {
          checkedDiv.top = this.y_axis_[j - 1] + e.target.offsetTop
          checkedDiv.height = this.y_axis_[j] - this.y_axis_[j - 1]
          break
        }
      }
      checkedDiv.x = this.xAxis[i - 1]
      checkedDiv.y = this.yAxis[this.yAxis.length - j - 1]
      checkedDiv.value = this.mapData[j - 1][i - 1].toExponential(5)
      return checkedDiv
    },
    getMaxMin (data) {
      let dataMaxs = []
      let dataMins = []
      for (let i = 0; i < data.length; i++) {
        dataMaxs.push(Math.max.apply(null, data[i]))
        dataMins.push(Math.min.apply(null, data[i]))
      }
      let dataMax = this.dataMax = Math.max.apply(null, dataMaxs)
      let dataMin = this.dataMin = Math.min.apply(null, dataMins)
      return [dataMax, dataMin]
    }
  }
}
</script>

<!-- Add 'scoped' attribute to limit CSS to this component only -->
<style scoped>
.x-axis {
  font-size: 12px;
  border-top: 1px solid;
  margin-top: -6px;
}
.x-axis .ruler {
  border-left: 1px solid #8f8d8d;
  height: 5px;
}
.x-axis .values {
  height: 5px;
  margin-top: 4px;
}
.y-axis {
  font-size: 12px;
  float: left;
  width: 12%;
  min-width: 55px;
  max-width: 55px;
}
.y-axis .ruler {
  width: 4px;
  border-right: 1px solid#8f8d8d;
  float: right;
}
.ruler div {
  position: absolute;
  border-top: 1px solid #8f8d8d;
  width: 5px;
}
.y-axis .values {
  width: 50px;
  height: 100%;
  float: left;
}
.values .value {
  width: 50px;
  position: absolute;
  text-align: right;
}
.x-axis span {
  position: absolute;
  font-size: 12px;
}
.x-axis span span{
  font-size: 5px;
  position: relative;
  top: -10px;
}
.x-axis .x-grid {
  font-size: 12px;
  width: 41px;
  display: inline-block;
}
/* .y-axis div {
  font-size: 12px;
  text-align: right;
  width: 52px;
} */
.y-axis span {
  font-size: 8px;
  position: relative;
  top: -1px;
}
.canvas {
  float: right;
  width: 90%;
  height: 90%;
}
.checked-div {
  background: red;
  position: absolute;
  box-shadow:0 0 9px 3px #999;
  animation: anim 0.4s;
  transition: opacity 0.2s cubic-bezier(0.23, 1, 0.32, 1) 0s, visibility 0.2s cubic-bezier(0.23, 1, 0.32, 1) 0s, transform 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s;
}
.value-div {
  background: white;
  position: absolute;
  color: #000;
  padding: 2px 5px;
  border-radius: 3px;
  font-size: 14px;
  text-align: left;
}
@keyframes anim {
  0%{opacity: 0;}
  100%{opacity:1;}
}
.value-div span {
  font-weight: bold;
}
</style>
